﻿/*
Copyright © 2011 François Karman

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

See <license.txt> for the complete license of the software.
*/

namespace Simple.Storage.Api
{
    /// <summary>
    /// Implements an initialization process that can be used in any children class.
    /// </summary>
    /// <remarks>
    /// The initialization process is based on two methods:
    /// <list type="table">
    /// <item>
    /// <term><see cref="Initializable.Initialize"/></term>
    /// <description>initializes the current instance - if not already done.</description>
    /// </item>
    /// <item>
    /// <term><see cref="Initializable.AssertNotInitialized"/></term>
    /// <description>ensures that the instance is not already initialized.</description>
    /// </item>
    /// </list>
    /// <para>Those methods are thread-safe.</para>
    /// </remarks>
    public abstract class Initializable
    {
        /// <summary>
        /// The object used to synchronize multi-thread calls to the instance.
        /// </summary>
        private readonly object syncObject;

        /// <summary>
        /// A value indicating whether the current instance is already initialized.
        /// </summary>
        private bool initialized;

        /// <summary>
        /// Initializes a new instance of the Initializable class.
        /// </summary>
        protected Initializable()
        {
            this.syncObject = new object();
        }

        /// <summary>
        /// Gets a value indicating whether the current instance is already initialized.
        /// </summary>
        public bool Initialized
        {
            get { return this.initialized; }
        }

        /// <summary>
        /// Gets the synchronization object used by the current instance.
        /// </summary>
        protected object SyncObject
        {
            get { return this.syncObject; }
        }

        /// <summary>
        /// Ensures that the instance is not already initialized.
        /// </summary>
        /// <exception cref="AlreadyInitializedException">
        /// The current instance is already initialized.
        /// </exception>
        protected void AssertNotInitialized()
        {
            lock (this.syncObject)
            {
                if (this.initialized)
                {
                    string message = string.Format(Messages.Culture, Messages.AlreadyInitialized, this.GetType().Name, this);
                    throw new AlreadyInitializedException(message);
                }
            }
        }

        /// <summary>
        /// Initializes the current instance.
        /// </summary>
        protected void Initialize()
        {
            lock (this.syncObject)
            {
                if (this.initialized)
                {
                    return;
                }

                this.initialized = true;
                this.InternalInitialize();
            }
        }

        /// <summary>
        /// Implements the initialization process.
        /// </summary>
        protected abstract void InternalInitialize();
    }
}
